package com.xrui.hbaseview.ui.forms;

import com.google.common.base.Preconditions;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.michaelbaranov.microba.calendar.DatePicker;

import com.xrui.hbase.ColumnName;
import com.xrui.hbase.schema.RowKeyComponent;
import com.xrui.hbase.schema.TableLayout;
import com.xrui.hbase.tools.ToolUtils;
import com.xrui.hbaseview.data.ColumnFamily;
import com.xrui.hbaseview.data.TableDescriptor;
import com.xrui.hbaseview.format.DateUtils;
import com.xrui.hbaseview.hbase.Query;
import com.xrui.hbaseview.ui.controls.WideComboBox;
import com.xrui.hbaseview.ui.design.JCellEditor;
import com.xrui.hbaseview.ui.design.JTableModel;
import org.apache.hadoop.hbase.util.Bytes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyVetoException;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class ScanDialog extends JDialog {
    private static final Logger LOG = LoggerFactory.getLogger(ScanDialog.class);
    //region Constants
    private static final long serialVersionUID = 694275430107585525L;
    private static final int START_KEY_COLUMN = 2;
    private static final int END_KEY_COLUMN = 3;
    //endregion

    //region Variables
    private JTable tableRowKey;
    private DefaultTableModel tableModel;
    private WideComboBox columnNames;
    private ComboBoxModel columnNamesModel;
    private JPanel contentPane;
    private JButton buttonRun;
    private JButton buttonCancel;
    private JComboBox comboBoxMaxVersions;
    private DatePicker startTimeDatePicker;
    private DatePicker endTimeDatePicker;
    private JCheckBox checkBoxUseDates;
    private boolean okPressed;
    private Query validatedQuery;
    private TableLayout tableLayout;

    //region Constructor
    @SuppressWarnings("unchecked")
    public ScanDialog(Query query, TableLayout tableLayout, TableDescriptor tableDescriptor) {
        setContentPane(this.contentPane);
        setModal(true);
        setTitle("Scan");
        getRootPane().setDefaultButton(this.buttonRun);

        this.tableLayout = tableLayout;
        this.tableModel = new DefaultTableModel();
        this.tableRowKey.setModel(this.tableModel);

        this.tableModel.addColumn("Component");
        this.tableModel.addColumn("Hashed");
        this.tableModel.addColumn("Start");
        this.tableModel.addColumn("End");
        this.tableRowKey.setRowHeight(this.tableRowKey.getFont().getSize() + 8);
        this.tableRowKey.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
        this.tableRowKey.getColumn("Hashed").setPreferredWidth(5);

        this.tableRowKey.getColumn("Component").setCellEditor(new JCellEditor(null, false));
        this.tableRowKey.getColumn("Hashed").setCellEditor(new JCellEditor(null, false));
        this.tableRowKey.getColumn("Start").setCellEditor(new JCellEditor(null, true));
        this.tableRowKey.getColumn("End").setCellEditor(new JCellEditor(null, true));

        for (RowKeyComponent component : tableLayout.getRowKeyFormat().getComponents()) {
            String hashed = component.isHashed() ? "Yes" : "No";
            this.tableModel.addRow(new Object[]{component.getName(), hashed, "", ""});
        }

        for (ColumnName columnName : tableLayout.getColumns()) {
            this.columnNames.addItem(columnName);
        }

        this.startTimeDatePicker.setDateFormat(DateUtils.getDefaultDateFormat());
        this.endTimeDatePicker.setDateFormat(DateUtils.getDefaultDateFormat());

        loadMaxVersions(tableDescriptor);

        this.columnNames.addItemListener(
            e -> {
                if (e.getStateChange() == ItemEvent.SELECTED) {
                    loadMaxVersions(tableDescriptor);
                }
            });

        fillForm(query);
        toggleDateTimePicker();

        this.buttonRun.addActionListener(
            e -> onOK());

        this.buttonCancel.addActionListener(
            e -> onCancel());

        // call onCancel() when cross is clicked
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        addWindowListener(
            new WindowAdapter() {
                @Override
                public void windowClosing(WindowEvent e) {
                    onCancel();
                }
            });

        // call onCancel() on ESCAPE
        this.contentPane.registerKeyboardAction(
            e -> onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);

        checkBoxUseDates.addItemListener(e -> {
            toggleDateTimePicker();
        });
    }
    //endregion

    //region Public Methods
    public boolean showDialog(Component owner) {
        this.setComponentOrientation(owner.getComponentOrientation());
        this.pack();
        this.setResizable(false);
        this.setLocationRelativeTo(owner);
        this.setVisible(true);

        return this.okPressed;
    }

    public Query getQuery() throws IOException {
        if (this.okPressed) {
            return validatedQuery;
        }
        return null;
    }

    //region Private Methods
    private void toggleDateTimePicker() {
        if (checkBoxUseDates.isSelected()) {
            startTimeDatePicker.setEnabled(true);
            endTimeDatePicker.setEnabled(true);
        } else {
            startTimeDatePicker.setEnabled(false);
            endTimeDatePicker.setEnabled(false);
        }
    }

    @SuppressWarnings("unchecked")
    private void loadMaxVersions(TableDescriptor tableDescriptor) {
        Map<String, Integer> maxVersionMap = new HashMap<>();
        for (ColumnFamily columnFamily : tableDescriptor.families()) {
            Integer maxVersions = 1;
            try {
                String versions = String.valueOf(columnFamily.getMetadata().getOrDefault("VERSIONS", "1"));
                maxVersions = Integer.valueOf(versions);
            } catch (Exception ignore) {
            }
            maxVersionMap.put(columnFamily.getName(), maxVersions);
        }
        ColumnName columnName = (ColumnName) this.columnNames.getSelectedItem();
        int maxVersions = maxVersionMap.getOrDefault(columnName.getFamily(), 1);

        for (int i = 1; i <= maxVersions; i++) {
            this.comboBoxMaxVersions.addItem(i);
        }
    }

    private Map<String, String> getRowKeyComponents(int column) throws IOException {
        Map<String, String> components = new LinkedHashMap<>();
        for (int i = 0; i < tableRowKey.getRowCount(); i++) {
            String name = tableRowKey.getValueAt(i, 0).toString();
            Object cell = tableRowKey.getValueAt(i, column);

            boolean isNullOrEmpty = null == cell || cell.toString().isEmpty();
            String value = isNullOrEmpty ? null : cell.toString();
            components.put(name, value);
        }
        return components;
    }

    private void setRowKeyComponents(int column, Map<String, String> target) {
        for (int i = 0; i < tableRowKey.getRowCount(); i++) {
            String name = tableRowKey.getValueAt(i, 0).toString();
            if (target.containsKey(name)) {
                tableRowKey.setValueAt(target.get(name), i, column);
            }
        }
    }

    private void fillForm(Query query) {
        if (query != null) {
            if (query.getStartDate() != null) {
                this.checkBoxUseDates.setSelected(true);
                try {
                    this.startTimeDatePicker.setDate(query.getStartDate());
                    this.endTimeDatePicker.setDate(query.getEndDate());
                } catch (PropertyVetoException ignore) {
                }
            }

            if (query.getStartComponents() != null) {
                setRowKeyComponents(START_KEY_COLUMN, query.getStartComponents());
            }

            if (query.getEndComponents() != null) {
                setRowKeyComponents(END_KEY_COLUMN, query.getEndComponents());
            }

            if (query.getColumnWithFamily() != null) {
                this.comboBoxMaxVersions.setSelectedItem(ColumnName.create(query.getColumnWithFamily()));
            }

            if (query.getMaxVersions() > 0) {
                this.comboBoxMaxVersions.setSelectedItem(query.getMaxVersions());
            }
        }
    }

    private boolean validateQuery() throws IOException {
        Query query = new Query();

        Map<String, String> startKeyComponents = getRowKeyComponents(START_KEY_COLUMN);
        Map<String, String> endKeyComponents = getRowKeyComponents(END_KEY_COLUMN);

        if (this.checkBoxUseDates.isSelected()) {
            query.setStartDate(this.startTimeDatePicker.getDate());
            query.setEndDate(this.endTimeDatePicker.getDate());
        }

        int maxVersions = (int) this.comboBoxMaxVersions.getSelectedItem();
        ColumnName column = (ColumnName) this.columnNames.getSelectedItem();

        query.setFamily(column.getFamily());
        query.setColumn(column.getQualifier());
        query.setMaxVersions(maxVersions);
        query.setStartComponents(startKeyComponents);
        query.setEndComponents(endKeyComponents);

        ToolUtils.RowFinder startRowFinder = ToolUtils.populateRowKey(tableLayout, startKeyComponents);
        ToolUtils.RowFinder endRowFinder = ToolUtils.populateRowKey(tableLayout, endKeyComponents);

        query.setRowFilter(startRowFinder.getRowKeyFilter());

        Preconditions.checkArgument(endRowFinder.getRowKeyFilter() == null || endRowFinder.IsSpecified(),
            "End row key components should be fully filled or all empty"
        );
        byte[] startKey = startRowFinder.getRowKeyBytes();
        byte[] endKey = null;
        if (endRowFinder.IsSpecified()) {
            endKey = endRowFinder.getRowKeyBytes();

            if (null != startKey && Bytes.compareTo(startKey, endKey) > 0) {
                //end key less than the start key, need to be swapped
                endKey = startKey;
                startKey = endRowFinder.getRowKeyBytes();
            }
        } else if (startRowFinder.IsSpecified()) {
            endKey = startKey;
        }

        query.setStartKey(startKey);
        query.setEndKey(endKey);

        validatedQuery = query;
        return true;
    }

    //endregion

    private void onOK() {
        try {
            JTableModel.stopCellEditing(tableRowKey);
            validateQuery();
            this.okPressed = true;
            JTableModel.stopCellEditing(tableRowKey);

            dispose();
        } catch (Exception e) {
            LOG.error("Got invalid scan", e);
            JOptionPane.showMessageDialog(ScanDialog.this, e.getMessage(), "Invalid Scan", JOptionPane.ERROR_MESSAGE);
            validatedQuery = null;
            this.okPressed = false;
        }
    }

    private void onCancel() {
        // add your code here if necessary
        dispose();
    }

    {
// GUI initializer generated by IntelliJ IDEA GUI Designer
// >>> IMPORTANT!! <<<
// DO NOT EDIT OR ADD ANY CODE HERE!
        $$$setupUI$$$();
    }

    /**
     * Method generated by IntelliJ IDEA GUI Designer
     * >>> IMPORTANT!! <<<
     * DO NOT edit this method OR call it in your code!
     *
     * @noinspection ALL
     */
    private void $$$setupUI$$$() {
        contentPane = new JPanel();
        contentPane.setLayout(new GridLayoutManager(4, 1, new Insets(10, 10, 10, 10), -1, -1));
        contentPane.setMaximumSize(new Dimension(400, 350));
        final JPanel panel1 = new JPanel();
        panel1.setLayout(new GridLayoutManager(2, 2, new Insets(0, 0, 0, 0), -1, -1));
        contentPane.add(panel1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
        final JLabel label1 = new JLabel();
        label1.setText("Row Key");
        panel1.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final JScrollPane scrollPane1 = new JScrollPane();
        panel1.add(scrollPane1, new GridConstraints(1, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
        tableRowKey = new JTable();
        tableRowKey.setPreferredScrollableViewportSize(new Dimension(450, 100));
        scrollPane1.setViewportView(tableRowKey);
        final JToolBar toolBar1 = new JToolBar();
        toolBar1.setFloatable(false);
        panel1.add(toolBar1, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(-1, 20), null, 0, false));
        final JPanel panel2 = new JPanel();
        panel2.setLayout(new GridLayoutManager(7, 3, new Insets(5, 5, 5, 0), -1, -1));
        contentPane.add(panel2, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
        final JLabel label2 = new JLabel();
        label2.setText("Max Versions");
        panel2.add(label2, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        comboBoxMaxVersions = new JComboBox();
        panel2.add(comboBoxMaxVersions, new GridConstraints(4, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final JLabel label3 = new JLabel();
        label3.setText("End Time");
        panel2.add(label3, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final JLabel label4 = new JLabel();
        label4.setText("Start Time");
        panel2.add(label4, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        startTimeDatePicker = new DatePicker();
        panel2.add(startTimeDatePicker, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(-1, 26), null, 0, false));
        endTimeDatePicker = new DatePicker();
        panel2.add(endTimeDatePicker, new GridConstraints(2, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(-1, 26), null, 0, false));
        checkBoxUseDates = new JCheckBox();
        checkBoxUseDates.setText("Use Dates");
        panel2.add(checkBoxUseDates, new GridConstraints(1, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final JLabel label5 = new JLabel();
        label5.setText("Column Name");
        panel2.add(label5, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        columnNames = new WideComboBox();
        columnNames.setEditable(false);
        columnNames.setToolTipText("Choose the column name (column family and qualifier)");
        panel2.add(columnNames, new GridConstraints(3, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(-1, 20), null, 0, false));
        final JPanel panel3 = new JPanel();
        panel3.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1));
        contentPane.add(panel3, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, 1, null, null, null, 0, false));
        final JPanel panel4 = new JPanel();
        panel4.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1, true, false));
        panel3.add(panel4, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_VERTICAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
        buttonRun = new JButton();
        buttonRun.setText("Run");
        panel4.add(buttonRun, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        buttonCancel = new JButton();
        buttonCancel.setText("Cancel");
        panel4.add(buttonCancel, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
        final JSeparator separator1 = new JSeparator();
        contentPane.add(separator1, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
    }

    /**
     * @noinspection ALL
     */
    public JComponent $$$getRootComponent$$$() {
        return contentPane;
    }
}
